package burubaka.bat;

import java.util.List;
import java.util.logging.Logger;

import burubaka.BurubakaConstants;
import burubaka.common.BurubakaUtil;
import burubaka.model.Handicapping;
import burubaka.model.MarkSheet;
import burubaka.model.PayOff;
import burubaka.model.RaceInfo;
import burubaka.model.TotalResult;
import burubaka.service.HandicappingService;
import burubaka.service.MarkSheetService;
import burubaka.service.PayOffService;
import burubaka.service.RaceInfoService;
import burubaka.service.TotalResultService;

import com.google.appengine.api.datastore.Key;

public class RacePayOffBat {

	private static final Logger log = Logger.getLogger(RacePayOffBat.class.getName());
	private RaceInfoService raceInfoService = new RaceInfoService();
	private HandicappingService handicappingService = new HandicappingService();
	private MarkSheetService markSheetService = new MarkSheetService();
	private TotalResultService totalResultService = new TotalResultService();
	private PayOffService payOffService = new PayOffService();
	private RankingOrderBat rankingOrderBat = new RankingOrderBat();

	/**
	 * 実行
	 * 
	 * @return raceInfo
	 * @throws Exception
	 */
	public RaceInfo execute() throws Exception {

		RaceInfo raceInfo = raceInfoService.getCurrentRaceInfo();

		// すでにレース情報が登録されているかどうか判断
		// まだ登録されていない場合は何もしない
		if (raceInfo == null) {
			return null;
		}

		// レース結果が登録された直後の場合は進捗状況を更新
		if (raceInfo.getProgress() == BurubakaConstants.PROGRESS_RACERESULT) {
			raceInfo.setProgress(BurubakaConstants.PROGRESS_PAYOFF_START);
			raceInfoService.regist(raceInfo);
		}

		// 配当計算が開始状態でなければ何もしない
		if (raceInfo.getProgress() != BurubakaConstants.PROGRESS_PAYOFF_START) {
			return raceInfo;
		}

		return execute(raceInfo);
	}

	/**
	 * 実行
	 * 
	 * @param raceInfo
	 * @return raceInfo
	 * @throws Exception
	 */
	public RaceInfo execute(RaceInfo raceInfo) throws Exception {
		// 配当取得
		PayOff payOff = payOffService.findByAncKey(raceInfo.getKey());
		if (payOff == null) {
			return raceInfo;
		}
		// 配当計算が完了していない現在予想一覧を取得
		List<Handicapping> handicappingList = handicappingService.getCalculatingHandicapping(raceInfo.getRaceDate());
		for (Handicapping handicapping : handicappingList) {
			calcHandicapping(handicapping, payOff);
		}

		// レース情報更新
		raceInfo.setProgress(BurubakaConstants.PROGRESS_PAYOFF_END);

		// 順位設定
		rankingOrderBat.execute();
		raceInfo.setProgress(BurubakaConstants.PROGRESS_END);
		raceInfoService.regist(raceInfo);

		return raceInfo;
	}

	/**
	 * 予想計算
	 * 
	 * @param handicapping
	 */
	public void calcHandicapping(Handicapping handicapping) {
		RaceInfo raceInfo = raceInfoService.findByRaceDate(handicapping.getRaceDate());
		if (raceInfo == null) {
			return;
		}
		PayOff payOff = payOffService.findByAncKey(raceInfo.getKey());
		// 強制的に計算対称にする
		handicapping.setPayOffFlag(BurubakaConstants.FLAG_ON);
		calcHandicapping(handicapping, payOff);
	}

	/**
	 * 予想計算
	 * 
	 * @param payOff
	 * @param handicapping
	 */
	private void calcHandicapping(Handicapping handicapping, PayOff payOff) {
		// 賭け金
		long wager = 0;
		// 回収
		long withdrawal = 0;
		Key tipsterKey = handicapping.getKey().getParent();

		// マークシート情報を取得
		List<MarkSheet> markSheetList = markSheetService.findByAncKey(handicapping.getKey());
		for (MarkSheet markSheet : markSheetList) {
			// 賭け金を加算
			long tempWager = markSheet.getWager() * 100;
			long tempWithDrawal = getWithDrawal(payOff, markSheet);
			markSheet.setWithdrawal(tempWithDrawal);
			markSheet.setWithdrawalRate(BurubakaUtil.getPercent(tempWithDrawal, tempWager));
			if (tempWithDrawal != 0) {
				markSheet.setHitFlag(BurubakaConstants.FLAG_ON);
			}
			markSheetService.regist(markSheet);
			wager = wager + tempWager;
			withdrawal = withdrawal + tempWithDrawal;
		}

		// 総合成績更新
		TotalResult totalResult = totalResultService.getThisYearResult(tipsterKey);
		long totalWager = totalResult.getWager() + wager;
		totalResult.setWager(totalWager);

		long totalWithdrawal = totalResult.getWithdrawal() + withdrawal;
		totalResult.setWithdrawal(totalWithdrawal);

		double totalWithdrawalRate = BurubakaUtil.getPercent(totalWithdrawal, totalWager);
		totalResult.setWithdrawalRate(totalWithdrawalRate);

		int totalBets = totalResult.getBets() + 1;
		totalResult.setBets(totalBets);

		int totalHit = totalResult.getHit();
		if (withdrawal != 0) {
			totalHit = totalHit + 1;
			totalResult.setHit(totalHit);
		}

		double totalHitRate = BurubakaUtil.getPercent(totalHit, totalBets);
		totalResult.setHitRate(totalHitRate);
		totalResultService.regist(totalResult);

		// 予想更新
		handicapping.setWager(wager);
		handicapping.setWithdrawal(withdrawal);
		handicapping.setWithdrawalRate(BurubakaUtil.getPercent(withdrawal, wager));
		handicapping.setPayOffFlag(BurubakaConstants.FLAG_OFF);
		handicappingService.regist(handicapping);
	}

	/**
	 * 払戻し金取得
	 * 
	 * @param payOff
	 * @param markSheet
	 * @return withDrawal
	 */
	private long getWithDrawal(PayOff payOff, MarkSheet markSheet) {
		// 馬連
		if (BurubakaConstants.BET_CLASS_QUINELLA[0].equals(markSheet.getBetClass())) {
			if (payOff.getQuinellaNo().equals(markSheet.getMark())) {
				return markSheet.getWager() * payOff.getQuinella();
			}
		}
		// 馬単
		if (BurubakaConstants.BET_CLASS_EXACTA[0].equals(markSheet.getBetClass())) {
			if (payOff.getExactaNo().equals(markSheet.getMark())) {
				return markSheet.getWager() * payOff.getExacta();
			}
		}
		// ３連複
		if (BurubakaConstants.BET_CLASS_TRIO[0].equals(markSheet.getBetClass())) {
			if (payOff.getTrioNo().equals(markSheet.getMark())) {
				return markSheet.getWager() * payOff.getTrio();
			}
		}
		// ３連単
		if (BurubakaConstants.BET_CLASS_TRIFECTA[0].equals(markSheet.getBetClass())) {
			if (payOff.getTrifectaNo().equals(markSheet.getMark())) {
				return markSheet.getWager() * payOff.getTrifecta();
			}
		}
		return 0;
	}

}
